package com.rede.didiok.user.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rede.didiok.common.constant.conf.MessageConf;
import com.rede.didiok.common.constant.conf.SQLConf;
import com.rede.didiok.common.constant.global.Constants;
import com.rede.didiok.common.domainEvent.EventAction;
import com.rede.didiok.common.dto.ReportOption;
import com.rede.didiok.common.dto.community.MomentCommentDto;
import com.rede.didiok.common.dto.community.UserMomentDto;
import com.rede.didiok.common.dto.rank.MemberCommentDto;
import com.rede.didiok.common.dto.rank.RankMemberDto;
import com.rede.didiok.common.dto.user.ReportDto;
import com.rede.didiok.common.enums.StatusEnum;
import com.rede.didiok.common.exception.exceptionType.BusinessException;
import com.rede.didiok.common.exception.exceptionType.FeignException;
import com.rede.didiok.common.exception.exceptionType.QueryException;
import com.rede.didiok.common.feign.CommunityFeignClient;
import com.rede.didiok.common.feign.RankFeignClient;
import com.rede.didiok.common.holder.RequestHolder;
import com.rede.didiok.common.utils.R;
import com.rede.didiok.common.utils.ResultUtil;
import com.rede.didiok.common.utils.StringUtils;
import com.rede.didiok.common.utils.business.DomainEventUtil;
import com.rede.didiok.user.dao.ReportDao;
import com.rede.didiok.user.entity.ReportEntity;
import com.rede.didiok.user.entity.UserEntity;
import com.rede.didiok.user.service.ReportService;
import com.rede.didiok.user.service.UserService;


@Service("reportService")
public class ReportServiceImpl extends ServiceImpl<ReportDao, ReportEntity> implements ReportService {

    @Resource
    ReportDao reportDao;
    @Resource
    UserService userService;
    @Resource
    RankFeignClient rankFeignClient;
    @Resource
    CommunityFeignClient communityFeignClient;
    @Resource
    private DomainEventUtil domainEventUtil;


    /***************************** admin:start **************************/
    // region  didiok-admin

    // endregion
    /***************************** admin:end **************************/



    /***************************** web:start **************************/
    // region didiok-web
    @Override
    public IPage<ReportEntity> getPageList(ReportDto reportDto) {

        QueryWrapper<ReportEntity> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(reportDto.getContent())) {
            queryWrapper.eq(SQLConf.CONTENT, reportDto.getContent());
        }
        queryWrapper.eq(SQLConf.STATUS, StatusEnum.ENABLE);
        Page<ReportEntity> page = new Page<>();
        page.setCurrent(reportDto.getCurrentPage());
        page.setSize(reportDto.getPageSize());
        queryWrapper.orderByDesc(SQLConf.CREATE_TIME);
        IPage<ReportEntity> iPage = reportDao.selectPage(page, queryWrapper);
        List<String> userUidList = new ArrayList<>();
        iPage.getRecords().forEach(item -> {
            userUidList.add(item.getCreateUserUid());
            userUidList.add(item.getReportUserUid());
        });
        Map<String, UserEntity> userMap = userService.getUserAvatarMapByIds(userUidList);
        iPage.getRecords().forEach(item -> {
            item.setUser(userMap.get(item.getCreateUserUid()));
            item.setReportUser(userMap.get(item.getReportUserUid()));
        });

        return iPage;
    }

    @Override
    public String batchDeleteReport(List<String> reportUidList) {
        if (reportUidList.size() == 0) {
            return ResultUtil.errorWithMessage(MessageConf.PARAM_INCORRECT);
        }
        List<ReportEntity> reportCollect = reportDao.selectBatchIds(reportUidList);
        reportCollect.forEach(item -> {
            item.setStatus(StatusEnum.DISABLED);
            domainEventUtil.publishEvent(EventAction.REPORT_DELETE, item);
        });

        return ResultUtil.successWithMessage(MessageConf.DELETE_SUCCESS);
    }

    @Override
    public Page<ReportDto> queryListByContent(ReportOption reportOption) {
        Page page = reportOption.creatPage();
        return reportDao.queryListByContent(page, reportOption);
    }

    @Override
    public String handleReport(String reportContentUid, String reportType, String reportUid, Integer progress) {
        List<ReportEntity> reportList = this.list(
                new LambdaQueryWrapper<ReportEntity>()
                        .eq(ReportEntity::getProgress, 0)
                        .eq(reportContentUid != null, ReportEntity::getReportContentUid, reportContentUid)
                        .eq(ReportEntity::getReportType, reportType)
                        .eq(reportUid != null, ReportEntity::getUid, reportUid)
        );


        if (reportList == null || reportList.size() == 0) {
            throw new BusinessException("未查询到该内容的举报信息");
        }


        reportList.forEach(item -> {
            item.setProgress(progress);
            domainEventUtil.publishEvent(EventAction.REPORT_HANDLE, item);
        });
        boolean isSave = this.updateBatchById(reportList);

        for (int i = reportList.size() - 1; i >= 0; i--) {
            ReportEntity report = reportList.get(i);
            /**
             * progress 值
             * 1为已查看 不做处理
             * 2为已处理
             */
            switch (progress) {
                case 1:
                    /**
                     *
                     * 暂不做处理
                     */
                    break;
                case 2:
                    /**
                     * 下架处理
                     */
                    this.revocationContent(report.getReportContentUid(), ReportType.getType(report.getReportType()));
                    break;
                default:
                    break;
            }
        }

        return ResultUtil.successWithMessage("处理成功");
    }

    @Override
    public Page<ReportDto> queryList(ReportOption reportOption) {
        Page page = reportOption.creatPage();

        return reportDao.queryList(page, reportOption);
    }

    @Override
    public ReportDto submitReport(ReportOption reportOption) {
        /**
         * 当前举报人
         */
        String userUid = RequestHolder.getUserUid();
        if (StringUtils.isEmpty(userUid)) {
            throw new QueryException(Constants.USER_UID_EMPTY);
        }
        ReportType type = ReportType.getType(reportOption.getReportType());
        if (type == null) {
            throw new BusinessException("举报类型错误,请重新核对后举报");
        }
        String reportUserId = null;
        /**
         * 根据类型获取举报的内容
         */
        switch (type) {

            case RANK_MEMBER:
                R memberResult = rankFeignClient.getRankMemberById(reportOption.getReportContentUid());
                RankMemberDto rankMemberDto;
                if (memberResult.getCode() == 0) {
                    rankMemberDto = memberResult.getData(new TypeReference<RankMemberDto>(){});
                }else {
                    throw new FeignException("feign调用异常（根据id获取榜单成员）：失败");
                }

                if (rankMemberDto == null) {
                    throw new BusinessException("该博客已被删除,感谢您为了社区健康环境做出贡献!");
                }
                // 检查该资源是否被举报过
                checkReportStatus(userUid, rankMemberDto.getUid(), type.getKey());
                reportUserId = rankMemberDto.getCreateUserUid();
                break;

            case MEMBER_COMMENT:
                R memberCommentResult = rankFeignClient.getCommentById(reportOption.getReportContentUid());
                MemberCommentDto memberCommentDto;
                if (memberCommentResult.getCode() == 0) {
                    memberCommentDto = memberCommentResult.getData(new TypeReference<MemberCommentDto>(){});
                }else {
                    throw new FeignException("feign调用异常（根据id获取榜单成员评论）：失败");
                }
                if (memberCommentDto == null) {
                    throw new BusinessException("该评论已被删除,感谢您为了社区健康环境做出贡献!");
                }
                // 检查该资源是否被举报过
                checkReportStatus(userUid, memberCommentDto.getUid(), type.getKey());
                reportUserId = memberCommentDto.getCreateUserUid();
                break;
            case MOMENT_COMMENT:
                R momentCommentResult = communityFeignClient.getCommentById(reportOption.getReportContentUid());
                MomentCommentDto momentCommentDto;
                if (momentCommentResult.getCode() == 0) {
                    momentCommentDto = momentCommentResult.getData(new TypeReference<MomentCommentDto>(){});
                }else {
                    throw new FeignException("feign调用异常（根据id获取榜单成员评论）：失败");
                }
                if (momentCommentDto == null) {
                    throw new BusinessException("该评论已被删除,感谢您为了社区健康环境做出贡献!");
                }
                // 检查该资源是否被举报过
                checkReportStatus(userUid, momentCommentDto.getUid(), type.getKey());
                reportUserId = momentCommentDto.getCreateUserUid();
                break;
            case USER_MOMENT:
                R momentResult = communityFeignClient.getUserMomentById(reportOption.getReportContentUid());
                UserMomentDto userMomentDto;
                if (momentResult.getCode() == 0) {
                    userMomentDto = momentResult.getData(new TypeReference<UserMomentDto>(){});
                }else {
                    throw new FeignException("feign调用异常（根据id获取榜单成员评论）：失败");
                }
                if (userMomentDto == null) {
                    throw new BusinessException("该动态已被删除,感谢您为了社区健康环境做出贡献!");
                }
                // 检查该资源是否被举报过
                checkReportStatus(userUid, userMomentDto.getUid(), type.getKey());
                reportUserId = userMomentDto.getCreateUserUid();
                break;
            default:
                throw new BusinessException("举报类型错误,请重新核对后举报");
        }
        ReportEntity report = new ReportEntity();

        report = report.build(reportOption,userUid, reportUserId);
        boolean isSave = this.save(report);
        // 发送举报领域事件
        if (isSave) {
            domainEventUtil.publishEvent(EventAction.REPORT_ADD, report);
        }
        /**
         * 下架操作
         */
        if (beyondTime(reportOption.getReportContentUid(), type)) {
            this.revocationContent(reportOption.getReportContentUid(), type);
        }
        ReportDto reportDto = new ReportDto();
        BeanUtils.copyProperties(report, reportDto);
        return reportDto;
    }

    // endregion
    /***************************** web:end **************************/

    /**
     * 检测举报次数是否超出阈值
     *
     * @param reportUid
     * @param reportType
     * @return
     */
    @Override
    public Boolean beyondTime(String reportUid, ReportType reportType) {
        Integer count = this.count(
                new LambdaQueryWrapper<ReportEntity>()
                        .eq(ReportEntity::getReportContentUid, reportUid)
                        .eq(ReportEntity::getReportType, reportType.getKey())
        );
        //todo 写死为10 后续加入举报限制配置  读取配置
        /**
         * count+1>=10
         * 新增前检测 所以是 默认count多+1
         */
        return count >= 10 ? true : false;
    }

    /**
     * 下架操作
     */
    @Override
    public Boolean revocationContent(String reportUid, ReportType reportType) {
        Boolean update = true;
        switch (reportType) {
            case RANK_MEMBER:
                R memberR = rankFeignClient.revocationRankMember(reportUid);

                if(memberR.getCode() == 0){
                    update = memberR.getData(new TypeReference<Boolean>(){});
                }else {
                    throw new FeignException("feign调用异常（下架操作）：失败");
                }
                break;
            case MEMBER_COMMENT:
                // 评论下架
                R memberCommentResult = rankFeignClient.revocationComment(reportUid);

                if(memberCommentResult.getCode() == 0){
                    update = memberCommentResult.getData(new TypeReference<Boolean>(){});
                }else {
                    throw new FeignException("feign调用异常（下架操作）：失败");
                }

                break;
            case MOMENT_COMMENT:
                // 评论下架
                R momentCommentResult = communityFeignClient.revocationComment(reportUid);

                if(momentCommentResult.getCode() == 0){
                    update = momentCommentResult.getData(new TypeReference<Boolean>(){});
                }else {
                    throw new FeignException("feign调用异常（下架操作）：失败");
                }

                break;
            case USER_MOMENT:
                // 用户动态下架
                R momentR = communityFeignClient.revocationMoment(reportUid);

                if(momentR.getCode() == 0){
                    update = momentR.getData(new TypeReference<Boolean>(){});
                }else {
                    throw new FeignException("feign调用异常（下架操作）：失败");
                }

                break;

        }
        return update;
    }

    /**
     * 检查该资源是否被举报过
     *
     * @param userUid
     * @param reportContentUid
     * @param reportType
     * @return
     */
    private void checkReportStatus(String userUid, String reportContentUid, String reportType) {
        Integer count = this.count(new LambdaQueryWrapper<ReportEntity>()
                .eq(ReportEntity::getCreateUserUid, userUid)
                .eq(ReportEntity::getReportContentUid, reportContentUid)
                .eq(ReportEntity::getReportType, reportType)
                .eq(ReportEntity::getStatus, StatusEnum.ENABLE)
        );
        if (count > 0) {
            throw new QueryException("你已举报过，请勿重复操作");
        }
    }


    public enum ReportType {
        PROBLEM("problem"),
        RANK_MEMBER("rank_member"),
        QUESTION("question"),
        MEMBER_COMMENT("member_comment"),
        MOMENT_COMMENT("moment_comment"),
        USER_MOMENT("user_moment");

        private String key;

        ReportType(String key) {
            this.key = key;
        }

        public static ReportType getType(String key) {
            for (ReportType info : ReportType.values()) {
                if (info.getKey().equals(key)) {
                    return info;
                }
            }
            return null;
        }

        public String getKey() {
            return this.key;
        }

    }
}